/*

    This file is part of Blue Violin.

    Blue Violin is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Blue Violin is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero Public License for more details.

    You should have received a copy of the GNU Affero Public License
    along with Blue Violin.  If not, see <http://www.gnu.org/licenses/>.

    Blue Violin  Copyright (C) 2008  Blue Violin, Inc. & Josh Watts <josh.watts@blueviolin.org>
	For more information, visit http://www.blueviolin.org/

    This program comes with ABSOLUTELY NO WARRANTY.
*/

#include "stdafx.h"
#include "RatchetBrowser.h"
#include "IEBrowser.h"

/////////////////////////////////////////////////////////////////////////////
// CIEBrowser
CIEBrowser::CIEBrowser()
{
	m_bWindowOnly = TRUE;	
	m_hDocumentComplete = CreateEvent(NULL, FALSE, FALSE, "DocumentComplete");
	m_hwndApp = 0;
	m_pPopupIDispatch = NULL;
	m_bstrBrowserID = "";
	m_pdispMaster = NULL;
	m_nMasterProcess = TRUE;
	m_nDispPtr = 0;
	m_bDispContinue = 0;
	m_MousedownIsMouseclick = FALSE;
	m_bIgnoreEvents = FALSE;
	m_nDispID1 = 0;
	m_nDispID2 = 0;

	// GDI+ initialization
	Gdiplus::GdiplusStartup(&m_nGdiPlusToken, &m_oGdiPlusStartupInput, NULL);

	// COM initialization
	CoInitialize(NULL);
}


CIEBrowser::~CIEBrowser()
{
	Gdiplus::GdiplusShutdown(m_nGdiPlusToken);
	CoUninitialize();
	return;
}


// 
// DWebBrowserEvents2 event handlers
//

// need to figure out how to release our connections to elements in the DOM
STDMETHODIMP CIEBrowser::WebBrowserEvents2_BeforeNavigate2(IDispatch* pDisp, 
														   VARIANT *vURL /* BSTR */, 
														   VARIANT *varFlags /* VT_I4 */, 
														   VARIANT *varTargetFrameName /* BSTR */, 
														   VARIANT *varPostData /* VT_BYREF | VT_VARIANT */, 
														   VARIANT *varHeaders /* BSTR */, 
														   VARIANT_BOOL *varCancel /* VT_BOOL | VT_BYREF */)
{
	HRESULT hr;
	IUnknown *punkBrowser = NULL;
	IUnknown *punkDisp = NULL;
	IDispatch *pdispDoc = NULL;
	IWebBrowser2 *_pwebTemp = NULL;
	IUnknown *_punkTemp = NULL;
	VECTORIUNKNOWNPOINTER::iterator iteratorTemp;
	long nFound = 0;
	IInternetSecurityManager *piInetSecMgr;
	DWORD dwZone;
	_bstr_t _bstrURL(vURL);


	// http://msdn2.microsoft.com/en-us/library/ms537133.aspx
	hr = CoCreateInstance(CLSID_InternetSecurityManager,
						  NULL,
						  CLSCTX_ALL,
						  IID_IInternetSecurityManager,
						  (void**) &piInetSecMgr);
	if (SUCCEEDED(hr))
	{
		// ZONE constants
		// 0 == Local
		// 1 == Intranet
		// 2 == Trusted
		// 3 == Internet
		// 4 == Restricted
		piInetSecMgr->MapUrlToZone(_bstrURL, &dwZone, 0);
		piInetSecMgr->Release();
		if (0 == dwZone ||
			4 == dwZone)
		{
			*varCancel = VARIANT_TRUE;
			return S_OK;
		}
	}

	hr = m_spIEBrowser->QueryInterface(IID_IUnknown, (void**)&punkBrowser);
	if (SUCCEEDED(hr))
	{

		hr = pDisp->QueryInterface(IID_IUnknown, (void**)&punkDisp);
		if (SUCCEEDED(hr))
		{

			// if if punkBrowser & punkDisp are equal to each other, fire the DocumentStart event
			Fire_DocumentStart(pDisp, vURL);

// 			// push the document's IUknown pointer into the vector
// 			// we'll use this during the DocumentComplete event to see
// 			// if all of the documents are completed before we fire the DocumentComplete event
// 			// -- I had to do this to handle frames and iframes
// 			// -- we search the vector to see if the IUknown pointer of the document
// 			//    already exists - if it does, we don't add it 
// 			//    redirects cause a document to change navigation and if we don't check the 
// 			//    vector, we'll add the same document twice
// 			for (iteratorTemp = vectorDocuments.begin();
// 				 iteratorTemp != vectorDocuments.end();
// 				 iteratorTemp++)
// 			{
// 				 _punkTemp = *iteratorTemp;
// 				 if (_punkTemp == punkDisp)
// 				 {
// 					 nFound = 1;
// 					 break;
// 				 }
// 			}	
// 			if (0 == nFound)
// 			{
// 				vectorDocuments.push_back(punkDisp);
// 			}

		}
	}
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_CommandStateChange(long nCommand, VARIANT_BOOL vbEnable)
{
	Fire_CommandStateChange(nCommand, vbEnable);
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_DocumentComplete(IDispatch* pDisp, VARIANT* URL)
{
	//Fire_DocumentComplete(pDisp, URL);
	HRESULT hr;
	IUnknown *punkBrowser = NULL;
	IUnknown *punkDisp = NULL;
	IUnknown *_punkTemp = NULL;
	IDispatch *pdispDoc = NULL;
	MSHTML::IHTMLDocument2 *pdocHtmlDoc = NULL;
	IWebBrowser2 *_pwebTemp = NULL;
	VECTORIUNKNOWNPOINTER::iterator iteratorTemp;
    // Is this the DocumentComplete event for the top frame window?
    // Check COM identity: compare IUnknown interface pointers.
    hr = m_spIEBrowser->QueryInterface(IID_IUnknown, (void**)&punkBrowser);
	if (SUCCEEDED(hr))
	{
		hr = pDisp->QueryInterface(IID_IUnknown, (void**)&punkDisp);
		if (SUCCEEDED(hr))
		{
			// walk the vector to remove the document that was completed
			for (iteratorTemp = vectorDocuments.begin();
				 iteratorTemp != vectorDocuments.end();
				 iteratorTemp++)
			{
				_punkTemp = *iteratorTemp;
				if (_punkTemp == punkDisp)
				{
					vectorDocuments.erase(iteratorTemp);
					break;
				}
			}
			// process the document's DOM
			// get the interace to the WebBrowser2 control
			// all documents & frames are instantiations of the IWebBrowser2 control
			hr = pDisp->QueryInterface(__uuidof(IWebBrowser2), (void**)&_pwebTemp);
			if (SUCCEEDED(hr))
			{
				// get the control's document's IDispatch interface
				hr = _pwebTemp->get_Document(&pdispDoc);
				if (SUCCEEDED(hr))
				{
					// finally we can get the interface to the document
					pdispDoc->QueryInterface(__uuidof(MSHTML::IHTMLDocument2), (void**)&pdocHtmlDoc);
					ProcessDocument(pdocHtmlDoc);
					pdocHtmlDoc->Release();
					_pwebTemp->Release();
				}
			}
			// don't fire the DocumentComplete event until all of the 
			// documents have been processed
			// -- I had to do this to handle frames and iframes
			if (0 == vectorDocuments.size())
			{
				// This is the DocumentComplete event for the top frame.
                // This page is loaded, so we can access the DHTML Object Model.
				//ProcessDOM();
				SetEvent(m_hDocumentComplete);
				PostMessage(RIE_DOCCOMPLETE, 0);
				Fire_DocumentComplete(pDisp, URL);
			}
			punkDisp->Release();
		}
		punkBrowser->Release();
	}
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_NavigateComplete2(IDispatch * pDisp, VARIANT* URL)
{
	IUnknown *punkBrowser = NULL;
	IUnknown *punkDisp = NULL;
	HRESULT hr;
	BSTR bstrURL;
	_bstr_t _bstrURL;
	VECTORIUNKNOWNPOINTER::iterator iteratorTemp;
	IUnknown *_punkTemp = NULL;
	long nFound = 0;

	if (VT_BSTR == URL->vt)
	{
		bstrURL = URL->bstrVal;
		_bstrURL = bstrURL;	
		hr = m_spIEBrowser->QueryInterface(IID_IUnknown, (void**)&punkBrowser);
		if (SUCCEEDED(hr))
		{
			hr = pDisp->QueryInterface(IID_IUnknown, (void**)&punkDisp);
			if (SUCCEEDED(hr))
			{
				// push the document's IUknown pointer into the vector
				// we'll use this during the DocumentComplete event to see
				// if all of the documents are completed before we fire the DocumentComplete event
				// -- I had to do this to handle frames and iframes
				// -- we search the vector to see if the IUknown pointer of the document
				//    already exists - if it does, we don't add it 
				//    redirects cause a document to change navigation and if we don't check the 
				//    vector, we'll add the same document twice
				for (iteratorTemp = vectorDocuments.begin();
					 iteratorTemp != vectorDocuments.end();
					 iteratorTemp++)
				{
					_punkTemp = *iteratorTemp;
					if (_punkTemp == punkDisp)
					{
						nFound = 1;
						break;
					}
				}	
				if (0 == nFound)
				{
					vectorDocuments.push_back(punkDisp);
				}

				if (punkBrowser == punkDisp)
				{
					Fire_NavigateComplete2(bstrURL);
				}
			}
		}
	}
	return S_OK;
}

STDMETHODIMP CIEBrowser::WebBrowserEvents2_NewWindow2(IDispatch *pDisp, VARIANT_BOOL bCancel)
{
	/*
	//DWORD nResult;
	//MSG msgTemp;
	//Fire_GetNewWindow();
	HRESULT hr;
	IUnknown *punkPopupBrowser = NULL;
	IDispatch *pdispPopupBrowser = NULL;
	IDispatch *pdispTemp = NULL;
	// TODO : Add Code for message handler. Call DefWindowProc if necessary.
	hr = m_spIEBrowser->put_Visible(VARIANT_BOOL(0));
	_bstr_t bstrProgID = "InternetExplorer.WebBrowser";
	AtlAxCreateControlEx(bstrProgID, m_hWnd, NULL, NULL, &punkPopupBrowser, IID_NULL, NULL);
	punkPopupBrowser->QueryInterface(IID_IDispatch, (void**)&pDisp);
	bCancel = VARIANT_FALSE;
	return S_OK;



	//CAxWindow wnd(m_hWnd);
	//ModifyStyle(0, WS_HSCROLL | WS_VSCROLL | WS_TABSTOP);
	//HRESULT hr = wnd.CreateControl(bstrProgID, NULL, NULL);
	//HRESULT hr = wnd.CreateControl(IDR_HTML1, NULL, NULL);
	//if (SUCCEEDED(hr))
	//	hr = wnd.QueryControl(IID_IWebBrowser2, (void**)&m_spIEPopupBrowser);
	//m_spIEBrowser->QueryInterface(IID_IDispatch, (void**)&pdispTemp);
	//m_spIEPopupBrowser->QueryInterface(IID_IDispatch, (void**)&pDisp);
	//pDisp = pdispPopupBrowser;
	//IDispEventSimpleImpl<1, CIEBrowser, &__uuidof(DWebBrowserEvents2)>::DispEventAdvise(m_spIEBrowser);
	//
	//g_hookTabHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgHookTab, NULL, GetCurrentThreadId());
	//
	//g_spIEBrowser = m_spIEBrowser;
	//Fire_BrowserCreated();
	//return S_OK;
	*/
	/*
	while (nResult = GetMessage(&msgTemp, NULL, 0, 0))
	{
		if (-1 == nResult ||
			0 == nResult)
		{
			break;
		}
		//else if (RIE_POPUPIDISPATCH == msgTemp.message)
		else if (NULL != m_ppPopupIDispatch)
		{
			break;
		}
		TranslateMessage(&msgTemp); 
		DispatchMessage(&msgTemp); 
	}
	pDisp = *m_ppPopupIDispatch;
	m_ppPopupIDispatch = NULL;
	*/
	//return S_OK;
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_NewWindow3(IDispatch **&pDisp, VARIANT_BOOL bCancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrURL)
{
	DWORD nResult;
	MSG msgTemp;
	_bstr_t _bstrURL = bstrURL;
	_bstr_t _bstrURLContext = bstrUrlContext;
	// fire an event to tell the application we need a new browser
	Fire_GetNewWindow(m_bstrBrowserID.copy());
	// spin in a message loop until we get a message telling us the new browser has been created
	while (nResult = GetMessage(&msgTemp, NULL, 0, 0))
	{
		if (-1 == nResult ||
			0 == nResult)
		{
			break;
		}
		else if (RIE_POPUPIDISPATCH == msgTemp.message)
		{
			break;
		}
		TranslateMessage(&msgTemp);
		DispatchMessage(&msgTemp);
	}
	// 
	m_pPopupIDispatch->QueryInterface(IID_IDispatch, (void**)&pDisp);
	m_pPopupIDispatch = NULL;
	//Fire_BrowserDisappear();
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_OnQuit()
{
	int i = 0;
	i = 123;
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_ProgressChange(long nProgressChange, long nProgressChangeMax)
{
	Fire_ProgressChange(nProgressChange, nProgressChangeMax);
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_StatusTextChange(BSTR bstrText)
{
	Fire_StatusTextChange(bstrText);
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_TitleChange(BSTR bstrText)
{
	Fire_TitleChange(bstrText);
	return S_OK;
}


STDMETHODIMP CIEBrowser::WebBrowserEvents2_WindowClosing(VARIANT_BOOL vbIsChildWindow, VARIANT_BOOL &vbCancel)
{
	Fire_WindowClosing(m_bstrBrowserID.copy());
	return S_OK;
}


//
// Methods which handle hooking/unhooking into the element events
//

STDMETHODIMP CIEBrowser::ReloadDOM()
{
	ReleaseDOM();
	ProcessDOM();
	return S_OK;
}


void CIEBrowser::ProcessDOM()
{
	HRESULT hr;
	IDispatch *pdispDoc = NULL;
	MSHTML::IHTMLDocument2 *pdocHtmlDoc = NULL;
	hr = m_spIEBrowser->get_Document(&pdispDoc);
	if (SUCCEEDED(hr))
	{
		// Obtained the document object.
		pdispDoc->QueryInterface(__uuidof(MSHTML::IHTMLDocument2), (void**)&pdocHtmlDoc);
		if (SUCCEEDED(hr))
		{
			// Obtained the IHTMLDocument2 interface for the document object
            ProcessDocument(pdocHtmlDoc);
		}
		pdispDoc->Release();
	}
}


void CIEBrowser::ReleaseDOM()
{
	HRESULT hr;
	IDispatch *pdispDoc;
	MSHTML::IHTMLDocument2 *phtmlDocument;
	hr = m_spIEBrowser->get_Document(&pdispDoc);
	if (SUCCEEDED(hr))
	{
		hr = pdispDoc->QueryInterface(__uuidof(MSHTML::IHTMLDocument2), (void**)&phtmlDocument);
		if (SUCCEEDED(hr))
		{
			ReleaseDocument(phtmlDocument);
		}
		pdispDoc->Release();
	}
}


void CIEBrowser::ProcessDocument(MSHTML::IHTMLDocument2* pDoc)
{
	HRESULT hr = 0;

	
	MSHTML::IHTMLElementCollection* pElemColl = NULL;
	MSHTML::IHTMLFramesCollection2 *pFrameColl = NULL;
	long nFrameCollLength = 0;

	IUnknown *punkThis = NULL;
	IConnectionPointContainer *piCPC = NULL;
	IConnectionPoint *piCP = NULL;
	DWORD nCookie = 0;

	hr = this->QueryInterface(IID_IUnknown, (void**)&punkThis);
	hr = pDoc->QueryInterface(IID_IConnectionPointContainer, (void**)&piCPC);
	hr = piCPC->FindConnectionPoint(MSHTML::DIID_HTMLDocumentEvents2, &piCP);
	hr = piCP->Advise(punkThis, &nCookie);

    hr = pDoc->get_all(&pElemColl);
    if (SUCCEEDED(hr))
    {
        // Obtained element collection.
        ProcessElementCollection(pElemColl);
        pElemColl->Release();
    }
	// process frames, if any
	hr = pDoc->get_frames(&pFrameColl);
	if (SUCCEEDED(hr))
	{
		pFrameColl->get_length(&nFrameCollLength);
		//
		MSHTML::IHTMLFrameElement2 *pframeElement = NULL;
		MSHTML::IHTMLWindow2 *pwinWindow = NULL;
		IDispatch *pdispElement = NULL;
		MSHTML::IHTMLDocument2 *pdocHtmlDoc;		
		_variant_t _varIndex;
		VARIANT tVar;
		VARIANT varFrameColl;
		for (long i = 0; 
			 i < nFrameCollLength; 
			 ++i)
		{
			_varIndex = i;
			tVar = _varIndex.Detach();
			hr = pFrameColl->item(&tVar, &varFrameColl);
			if (SUCCEEDED(hr))
			{
				hr = varFrameColl.pdispVal->QueryInterface(MSHTML::IID_IHTMLWindow2, (void**)&pwinWindow);
				if (SUCCEEDED(hr))
				{
					hr = pwinWindow->get_document(&pdocHtmlDoc);
					if (SUCCEEDED(hr))
					{
						ProcessDocument(pdocHtmlDoc);
					}
					// would like to add code here to figure out why the get_document failed
				}
			}
		}
	}
}


void CIEBrowser::ProcessElementCollection(MSHTML::IHTMLElementCollection* pElemColl)
{
	HRESULT hr;
    IDispatch* pdispElement = NULL;
	MSHTML::IHTMLElement *phtmlElement = NULL;
	MSHTML::IHTMLElement2 *phtmlElement2 = NULL;
	long nCollectionLength;
	long i;
	_variant_t varID;
	_variant_t varIndex;
	_bstr_t bstrTagName;
	_bstr_t bstrTagID;
	pElemColl->get_length(&nCollectionLength);
	for (i = 0; i < nCollectionLength; i++)
	{
		varID = i;
		varIndex = i;
		hr = pElemColl->item(varID, varIndex, &pdispElement);
		if (SUCCEEDED(hr))
		{
			hr = pdispElement->QueryInterface(MSHTML::IID_IHTMLElement, (void**)&phtmlElement);
			hr = pdispElement->QueryInterface(MSHTML::IID_IHTMLElement2, (void**)&phtmlElement2);
			if (SUCCEEDED(hr))
			{
				ConnectEvents(phtmlElement2);
				phtmlElement->Release();
				phtmlElement2->Release();
			}
			//pDisp->Release();
			pdispElement->Release();
		}
	}
}


void CIEBrowser::ConnectEvents(MSHTML::IHTMLElement2 *pElem)
{
    HRESULT hr;
	IUnknown *pUnk;
    IConnectionPointContainer* pCPC = NULL;
    IConnectionPoint* pCP = NULL;
	MSHTML::IHTMLElement *_phtmlElement = NULL;
	// MAPADVISETOKENS::iterator mapAdviseTokensIterator;
    DWORD dwCookie = 0;
	_bstr_t _bstrTag, _bstrType;
	// get the CIEBrowser IUnknown interface
	hr = this->QueryInterface(IID_IUnknown, (void**)&pUnk);
	if (SUCCEEDED(hr))
	{
		// Check that this is a connectable object.
		hr = pElem->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);

		if (SUCCEEDED(hr))
		{
			pElem->QueryInterface(MSHTML::IID_IHTMLElement, (void**)&_phtmlElement);
			GetTagAndType(_phtmlElement, &_bstrTag, &_bstrType);
			// Find the connection point.
			if (_bstrTag == _bstr_t(L"INPUT") &&
				(_bstrType == _bstr_t(L"text") ||
				 _bstrType == _bstr_t(L"password") ||
				 _bstr_t(L"file") == _bstrType))
			{
				hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLInputTextElementEvents2, &pCP);
			}
/*
			else if (_bstr_t(L"INPUT") == _bstrTag &&
					 _bstr_t(L"file") == _bstrType)
			{
				hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLInputFileElementEvents2, &pCP);
			}
*/
			else if (_bstrTag == _bstr_t(L"FORM"))
			{
				hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLFormElementEvents2, &pCP);
			}
			else if (_bstrTag == _bstr_t(L"SELECT"))
			{
				hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLSelectElementEvents2, &pCP);
			}
			else if (_bstrTag == _bstr_t(L"BODY") ||
					 _bstrTag == _bstr_t(L"P"))
			{
				hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLTextContainerEvents2, &pCP);
			}
			else
			{
				hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLElementEvents2, &pCP);
			}

			if (SUCCEEDED(hr))
			{
				// Advise the connection point.
				// pUnk is the IUnknown interface pointer for your event sink
				//pElem->get_sourceIndex(&elementSourceIndex);
				/*
				mapAdviseTokensIterator = mapAdviseTokens.find(elementSourceIndex);
				if (mapAdviseTokensIterator != mapAdviseTokens.end())
				{
					int foo = 43;
				}
				*/
				hr = pCP->Advise(pUnk, &dwCookie);	
				if (SUCCEEDED(hr))
				{
					// Successfully advised
					// m_mapAdviseCookies.insert(pUnk, dwCookie);
				}
				pCP->Release();
			}
			else
			{
				// else try try again
				hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLElementEvents, &pCP);
			}
			pCPC->Release();
		}
	}
} 


void CIEBrowser::ReleaseDocument(MSHTML::IHTMLDocument2 *phtmlDocument)
{
// 	HRESULT hr;
// 	MSHTML::IHTMLElementCollection *phtmlElementCollection = NULL;
// 	hr = phtmlDocument->get_all(&phtmlElementCollection);
// 	if (SUCCEEDED(hr))
// 	{
// 		ReleaseElementCollection(phtmlElementCollection);
// 		phtmlElementCollection->Release();
// 	}
	MAPADVISE::iterator oIterator;
	for (oIterator = m_mapAdviseCookies.begin();
		 oIterator != m_mapAdviseCookies.end();
		 oIterator++)
	{
		
	}
	

}



void CIEBrowser::ReleaseElementCollection(MSHTML::IHTMLElementCollection* pElemColl)
{
	HRESULT hr;
    IDispatch* pdispElement = NULL;
	MSHTML::IHTMLElement *phtmlElement = NULL;
	MSHTML::IHTMLElement2 *phtmlElement2 = NULL;
	long nCollectionLength;
	long i;
	_variant_t varID;
	_variant_t varIndex;
	_bstr_t bstrTagName;
	_bstr_t bstrTemp = "A";
	BSTR tbstrString;
	pElemColl->get_length(&nCollectionLength);
	for (i = 0; i < nCollectionLength; i++)
	{
		varID = i;
		varIndex = (long)0;
		hr = pElemColl->item(varID, varIndex, &pdispElement);
		if (SUCCEEDED(hr))
		{
			hr = pdispElement->QueryInterface(MSHTML::IID_IHTMLElement, (void**)&phtmlElement);
			//hr = pdispElement->QueryInterface(MSHTML::IID_IHTMLElement2, (void**)&phtmlElement2);
			if (SUCCEEDED(hr))
			{
				phtmlElement->get_tagName(&tbstrString);
				bstrTagName = tbstrString;
				//IDispEventSimpleImpl<1, CIEBrowser, &__uuidof(MSHTML::HTMLElementEvents2)>::DispEventAdvise(phtmlElement);
				DisconnectEvents(phtmlElement);
				phtmlElement->Release();
			}
			//pDisp->Release();
			pdispElement->Release();
		}
	}
	// mapAdviseTokens.clear();
}





void CIEBrowser::DisconnectEvents(MSHTML::IHTMLElement *pElem)
{
    HRESULT hr;
	IUnknown *pUnk;
    IConnectionPointContainer* pCPC = NULL;
    IConnectionPoint* pCP = NULL;
    DWORD dwCookie = 0;
	// MAPADVISETOKENS::iterator mapiterAdviseToken;
	long elementSourceIndex;

	// get the CIEBrowser IUnknown interface
	hr = this->QueryInterface(IID_IUnknown, (void**)&pUnk);
	if (SUCCEEDED(hr))
	{
		// Check that this is a connectable object.
		hr = pElem->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);

		if (SUCCEEDED(hr))
		{
			// Find the connection point.
			hr = pCPC->FindConnectionPoint(MSHTML::DIID_HTMLElementEvents2, &pCP);

			if (SUCCEEDED(hr))
			{
				pElem->get_sourceIndex(&elementSourceIndex);
				// dwCookie = mapAdviseTokens[elementSourceIndex];
				hr = pCP->Unadvise(dwCookie);
				if (SUCCEEDED(hr))
				{
					// Successfully advised
				}
				pCP->Release();
			}

			pCPC->Release();
		}
	}
} 


STDMETHODIMP CIEBrowser::CheckDocumentComplete(BOOL *pbVal)
{
	IDispatch *pdispDoc2 = NULL;
	MSHTML::IHTMLDocument2 *piDoc2 = NULL;
	m_spIEBrowser->get_Document(&pdispDoc2);
	pdispDoc2->QueryInterface(__uuidof(MSHTML::IHTMLDocument2),
							  (void**) &piDoc2);
	*pbVal = CheckDocumentComplete(piDoc2);
	piDoc2->Release();
	pdispDoc2->Release();
	return S_OK;
}


BOOL CIEBrowser::CheckDocumentComplete(MSHTML::IHTMLDocument2 *pdocRoot)
{
	HRESULT hr;
	long nFramesLength = 0;
	CComBSTR cbstrReadyState;
	VARIANT varIndex;
	VARIANT varFrame;
	BOOL bReturn = FALSE;
	MSHTML::IHTMLFramesCollection2 *pframesColl = NULL;
	MSHTML::IHTMLWindow2 *pwinWindow = NULL;
	MSHTML::IHTMLDocument2 *pdocFrameDoc = NULL;

	VariantInit(&varIndex);
	VariantInit(&varFrame);
	pdocRoot->get_readyState(&(cbstrReadyState.m_str));
	if (_bstr_t(L"complete") == _bstr_t(cbstrReadyState.m_str, TRUE))
	{
		bReturn = TRUE;
	}
	hr = pdocRoot->get_frames(&pframesColl);
	if (SUCCEEDED(hr))
	{
		pframesColl->get_length(&nFramesLength);

		varIndex.vt = VT_I4;
		for (long i = 0;
			 i < nFramesLength;
			 ++i)
		{
			varIndex.lVal = i;
			pframesColl->item(&varIndex, &varFrame);
			if (NULL != varFrame.pdispVal)
			{
				varFrame.pdispVal->QueryInterface(MSHTML::IID_IHTMLWindow2, (void**)&pwinWindow);
				pwinWindow->get_document(&pdocFrameDoc);
				if (NULL != pdocFrameDoc)
				{
					bReturn &= CheckDocumentComplete(pdocFrameDoc);
					pwinWindow->Release();
				}
			}
		}
		
	}
	VariantClear(&varIndex);
	VariantClear(&varFrame);
	return bReturn;
}


// Message numbers in the third range (0x8000 through 0xBFFF) are available for application to use 
// as private messages. Message in this range do not conflict with system messages. 
STDMETHODIMP CIEBrowser::WaitForDocumentComplete()
{
	HRESULT hr = 0;
	DWORD nStart = GetTickCount();
	DWORD nResult = 0;

	long nMilliseconds = 1000;
	long nFramesLength = 0;
// 	HANDLE phHandles[2];
	HANDLE phHandles[1];
	MSG msgTemp;
	MSHTML::IHTMLDocument2 *pdocRoot = NULL;
	IDispatch *pdispTemp = NULL;
	IOleWindow *piOleWindow = NULL;
	HWND hwndHandle(NULL);
	BOOL bDocComplete = FALSE;
	if (FALSE == bDocComplete)
	{
// 		hr = m_spIEBrowser->QueryInterface(__uuidof(IOleWindow), (void**)&piOleWindow);
// 		hr = piOleWindow->GetWindow(&hwndHandle);
// 		piOleWindow->Release();
		phHandles[0] = m_hDocumentComplete;

		while (1)
		{
			while (PeekMessage(&msgTemp, NULL, 0, 0, PM_REMOVE))
			{
				if (RIE_DOCCOMPLETE == msgTemp.message)
				{
					bDocComplete = TRUE;
					break;
				}
				else if (WM_QUIT == msgTemp.message)
				{
					break;
				}
				TranslateMessage(&msgTemp);
				DispatchMessage(&msgTemp);
			}
			if (TRUE == bDocComplete)
			{
				break;
			}
			// nResult = MsgWaitForMultipleObjects(2, phHandles, FALSE, nMilliseconds, QS_ALLINPUT);
			// nResult = MsgWaitForMultipleObjects(1, phHandles, FALSE, nMilliseconds, QS_ALLINPUT);

			nResult = MsgWaitForMultipleObjectsEx(1, phHandles, nMilliseconds, QS_INPUT, MWMO_INPUTAVAILABLE);


// 			if (WAIT_FAILED == nResult)
// 			{
// 				DWORD dwError = GetLastError();
// 				LPVOID szError = NULL;
// 				FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
// 							  NULL,
// 							  dwError,
// 							  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
// 							  (LPTSTR)&szError,
// 							  0,
// 							  NULL);
// 				int i = 123;
// 				LocalFree(szError);
// 			}
						  
		}
	}
	return S_OK;
}


























/*
all hail the almighty Invoke!
*/

STDMETHODIMP CIEBrowser::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, 
								VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
	IDispatchImpl<IIEBrowser, &IID_IIEBrowser, &LIBID_RATCHETBROWSERLib>::Invoke(dispidMember, riid, lcid, wFlags, 
																				 pdispparams, pvarResult, pexcepinfo, puArgErr);
	IDispEventSimpleImpl<1, CIEBrowser, &__uuidof(DWebBrowserEvents2)>::Invoke(dispidMember, riid, lcid, wFlags, 
																			   pdispparams, pvarResult, pexcepinfo, puArgErr);
	//#define DISPATCH_METHOD         0x1
	//#define DISPATCH_PROPERTYGET    0x2
	//#define DISPATCH_PROPERTYPUT    0x4
	//#define DISPATCH_PROPERTYPUTREF 0x8
	int i = 0;
	if (FALSE == m_bIgnoreEvents)
	{
		
		switch (dispidMember)
		{
			case DISPID_NAVIGATECOMPLETE2:
				i = 123;
				break;

// 		case DISPID_HTMLELEMENTEVENTS_ONACTIVATE:
// 			i = 123;
// 			break;

			case DISPID_HTMLELEMENTEVENTS_ONAFTERUPDATE:
				i = 123;
				break;

// 		case DISPID_HTMLELEMENTEVENTS_ONBEFOREACTIVATE:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_BEFORECOPY:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONBEFORECUT:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_BEFOREDEACTIVATE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONBEFOREEDITFOCUS:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_BEFOREPASTE:
// 			i = 123;
// 			break;
			
			case DISPID_HTMLELEMENTEVENTS_ONBEFOREUPDATE:
				i = 123;
				break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONBLUR:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_ONCELLCHANGE:
// 			i = 123;
// 			break;

			case DISPID_HTMLELEMENTEVENTS_ONCLICK:
				//OutputDebugString("\tonclick\n");
				//HTMLElementEvents_OnClick(pdispparams->rgvarg->pdispVal);
				break;

// 		case DISPID_HTMLELEMENTEVENTS_ONCONTEXTMENU:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONCONTROLSELECT:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONCOPY:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONCUT:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDATAAVAILABLE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDATASETCHANGED:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDATASETCOMPLETE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDBLCLICK:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDEACTIVATE:
// 			i = 1123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDRAG:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDRAGEND:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDRAGENTER:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDRAGLEAVE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDRAGOVER:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONDRAGSTART:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_ONDROP:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONERRORUPDATE:
// 			i = 123;
// 			break;
	
// 		case DISPID_HTMLELEMENTEVENTS_ONFILTERCHANGE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONFOCUS:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONFOCUSIN:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONFOCUSOUT:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONHELP:
// 			i = 123;
// 			break;
			
			case DISPID_HTMLELEMENTEVENTS_ONKEYDOWN:
				HTMLInputTextElementEvents_OnKeyDown(pdispparams->rgvarg->pdispVal);
				break;

			case DISPID_HTMLELEMENTEVENTS_ONKEYPRESS:
				HTMLInputTextElementEvents_OnKeyPress(pdispparams->rgvarg->pdispVal);
				break;

			case DISPID_HTMLELEMENTEVENTS_ONKEYUP:
				//HTMLInputTextElementEvents_OnKeyPress(pdispparams->rgvarg->pdispVal);
				i = 234;
				break;

// 		case DISPID_HTMLELEMENTEVENTS_ONLAYOUTCOMPLETE:
// 			i = 234;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONLOSECAPTURE:
// 			i = 123;
// 			break;

			case DISPID_HTMLELEMENTEVENTS_ONMOUSEDOWN:
				//i = 2345;
				m_nDispID2 = m_nDispID1;
				m_nDispID1 = DISPID_HTMLELEMENTEVENTS_ONMOUSEDOWN;
				HTMLElementEvents2_OnMouseDown(pdispparams->rgvarg->pdispVal);
				break;

// 		case DISPID_HTMLELEMENTEVENTS_ONMOUSEENTER:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_ONMOUSELEAVE:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_ONMOUSEMOVE:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_ONMOUSEOUT:
// 			i = 123;
// 			break;

			case DISPID_HTMLELEMENTEVENTS_ONMOUSEOVER:
				// HTMLElementEvents_OnMouseOver(pdispparams->rgvarg->pdispVal);
				break;
		
			case DISPID_HTMLELEMENTEVENTS_ONMOUSEUP:
				m_nDispID2 = m_nDispID1;
				m_nDispID1 = DISPID_HTMLELEMENTEVENTS_ONMOUSEUP;
				HTMLElementEvents2_OnMouseUp(pdispparams->rgvarg->pdispVal);
				break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONMOUSEWHEEL:
// 			i = 123;
// 			break;
			

// 		case DISPID_HTMLELEMENTEVENTS_ONMOVE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONMOVEEND:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_ONMOVESTART:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONPAGE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONPASTE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONPROPERTYCHANGE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONREADYSTATECHANGE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONRESIZE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONRESIZEEND:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONRESIZESTART:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONROWENTER:
// 			i = 123;
// 			break;

// 		case DISPID_HTMLELEMENTEVENTS_ONROWEXIT:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONROWSDELETE:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONROWSINSERTED:
// 			i = 123;
// 			break;
			
// 		case DISPID_HTMLELEMENTEVENTS_ONSCROLL:
// 			i = 123;
// 			break;

			case DISPID_HTMLTEXTCONTAINEREVENTS_ONSELECT:
				// HTMLTextContainerEvents_OnSelect(pdispparams->rgvarg->pdispVal);
				break;

			case DISPID_HTMLDOCUMENTEVENTS_ONSELECTIONCHANGE:
				HTMLDocumentEvents_OnSelect(pdispparams->rgvarg->pdispVal);
				break;
				

			
			case DISPID_HTMLELEMENTEVENTS_ONSELECTSTART:
				i = 123;
				break;

			// other events handled by this DISPID because they have the same value
			//
			// DISPID_HTMLSELECTELEMENTEVENTS_ONCHANGE
			// DISPID_HTMLFILEELELEMENTEVENTS_ONCHANGE
			case DISPID_HTMLINPUTTEXTELEMENTEVENTS_ONCHANGE:
				//HTMLSelectElementEvents_OnChange(pdispparams->rgvarg->pdispVal);
				HTMLElementEvents_OnChange(pdispparams->rgvarg->pdispVal);
				break;

			//case DISPID_HTMLSELECTELEMENTEVENTS_ONCHANGE:
			//	HTMLInputTextElementEvents_OnChange(pdispparams->rgvarg->pdispVal);
			//	break;

			case DISPID_HTMLFORMELEMENTEVENTS_ONSUBMIT:
				HTMLFormElementEvents_OnSubmit(pdispparams->rgvarg->pdispVal);
				break;

			default:
				i = 123;
				break;
		}
	}
    return S_OK;
}

void CIEBrowser::HTMLElementEvents_OnChange(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	MSHTML::IHTMLInputElement *pelemInput = NULL;
	BSTR bstrTagName;
	BSTR bstrTagType;
	_bstr_t _bstrTagName;
	_bstr_t _bstrTagType;
	//
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	pelemSrc->get_tagName(&bstrTagName);
	_bstrTagName = bstrTagName;
	if (_bstr_t(L"INPUT") == _bstrTagName)
	{
		pelemSrc->QueryInterface(MSHTML::IID_IHTMLInputElement, (void**)&pelemInput);
		pelemInput->get_type(&bstrTagType);
		_bstrTagType = bstrTagType;
		_wcsupr((wchar_t*)_bstrTagType);
		if (_bstr_t(L"TEXT") == _bstrTagType)
		{
			HTMLInputTextElementEvents_OnChange(pDisp);
		}
		else if (_bstr_t(L"FILE") == _bstrTagType)
		{
			HTMLFileElementEvents_OnChange(pDisp);
		}
		else // no type given - default to input text
		{
			HTMLInputTextElementEvents_OnChange(pDisp);
		}
		if (NULL != bstrTagType)
		{
			SysFreeString(bstrTagType);
		}
	}
	else if (_bstr_t(L"SELECT") == _bstrTagName)
	{
		HTMLSelectElementEvents_OnChange(pDisp);
	}
	if (NULL != bstrTagName)
	{
		SysFreeString(bstrTagName);
	}
	peventEvent->Release();
	pelemSrc->Release();
}


void CIEBrowser::HTMLElementEvents2_OnMouseDown(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	long nSourceIndex = 0;
	long bReturn = 0;
	//
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	bReturn = IsolateEvent((long)pDisp, pelemSrc, peventEvent);
	if (1 == bReturn)
	{
		peventEvent->Release();
		pelemSrc->Release();
		return;
	}
	if (VARIANT_TRUE == m_MousedownIsMouseclick)
	{
		Fire_ElementOnClick(pelemSrc, peventEvent);
		m_nOnClickID = -1;
	}
	else
	{
		pelemSrc->get_sourceIndex(&nSourceIndex);
		m_nOnClickID = nSourceIndex;
	}
	peventEvent->Release();
	pelemSrc->Release();
	return;
}


void CIEBrowser::HTMLElementEvents2_OnMouseUp(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	long nSourceIndex = 0;
	long bReturn = 0;
	//
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	bReturn = IsolateEvent((long)pDisp, pelemSrc, peventEvent);
	if (1 == bReturn)
	{
		peventEvent->Release();
		pelemSrc->Release();
		return;
	}
	pelemSrc->get_sourceIndex(&nSourceIndex);
	if (m_nOnClickID == nSourceIndex)
	{
		//HTMLElementEvents_OnClick(pDisp);
		Fire_ElementOnClick(pelemSrc, peventEvent);
		m_nOnClickID = -1;
	}
	peventEvent->Release();
	pelemSrc->Release();
	return;
}


void CIEBrowser::HTMLFormElementEvents_OnSubmit(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	//
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	Fire_FormOnSubmit(pelemSrc);
	peventEvent->Release();
	pelemSrc->Release();
}


void CIEBrowser::HTMLSelectElementEvents_OnChange(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	MSHTML::IHTMLSelectElement *pselectSrc = NULL;
	long nSelectedIndex = -1;
	//
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	pelemSrc->QueryInterface(MSHTML::IID_IHTMLSelectElement, (void**)&pselectSrc);
	pselectSrc->get_selectedIndex(&nSelectedIndex);
	Fire_SelectOnChange(pselectSrc, peventEvent);
	pelemSrc->Release();
	peventEvent->Release();
	pselectSrc->Release();
}


void CIEBrowser::HTMLFileElementEvents_OnChange(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	//
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	Fire_InputFileOnChange(pelemSrc, peventEvent);
	peventEvent->Release();
	pelemSrc->Release();
}


void CIEBrowser::HTMLInputTextElementEvents_OnKeyPress(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	long bReturn = 0;
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	bReturn = IsolateEvent((long)pDisp, pelemSrc, peventEvent);
	if (1 == bReturn)
	{
		peventEvent->Release();
		pelemSrc->Release();
		return;
	}
	Fire_KeyPress(pelemSrc, peventEvent);
    peventEvent->Release();
	pelemSrc->Release();
}


void CIEBrowser::HTMLInputTextElementEvents_OnKeyDown(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *peventEvent = NULL;
	MSHTML::IHTMLElement *pelemSrc = NULL;
	long nKeyCode = 0;
	long bReturn = 0;
	GetHTMLInterfacesFromIDispatch(pDisp, &peventEvent, &pelemSrc);
	peventEvent->get_keyCode(&nKeyCode);
//	if (0x8 == nKeyCode)
//	{	
		bReturn = IsolateEvent((long)pDisp, pelemSrc, peventEvent);
		if (1 == bReturn)
		{
			peventEvent->Release();
			pelemSrc->Release();
			return;
		}
		Fire_KeyDown(pelemSrc, peventEvent);
//	}
	peventEvent->Release();
	pelemSrc->Release();
}


void CIEBrowser::HTMLInputTextElementEvents_OnChange(IDispatch *pDisp)
{
	MSHTML::IHTMLEventObj *phtmlEventObj = NULL;
	MSHTML::IHTMLElement *phtmlElement= NULL;
	VARIANT varTagName;
	VARIANT varTagValue;
	_bstr_t _bstrTagName = "";
	_bstr_t _bstrTagValue = "";
	GetHTMLInterfacesFromIDispatch(pDisp, &phtmlEventObj, &phtmlElement);
	phtmlElement->getAttribute(L"name", 0, &varTagName);
	if (VT_BSTR == varTagName.vt)
	{
		_bstrTagName = varTagName.bstrVal;
	}
	phtmlElement->getAttribute(L"value", 0, &varTagValue);
	if (VT_BSTR == varTagValue.vt)
	{
		_bstrTagValue = varTagValue.bstrVal;
	}
	Fire_TextInputOnChange(_bstrTagName.copy(), _bstrTagValue.copy());

}


void CIEBrowser::HTMLElementEvents_OnMouseOver(IDispatch *pDisp)
{
	//HRESULT hr;
	MSHTML::IHTMLEventObj *phtmlEventObj = NULL;
	MSHTML::IHTMLElement *phtmlElement = NULL;
	MSHTML::IHTMLElement *phtmlTempElement = NULL;
	BSTR bstrTagName;
	_bstr_t _bstrTagName = "";
	BSTR bstrInnerText;
	//BSTR bstrTagType;
	_bstr_t _bstrTagType;
	VARIANT varTagType;
	BSTR bstrID;
	_bstr_t _bstrID;
	long nSourceIndex;
	GetHTMLInterfacesFromIDispatch(pDisp, &phtmlEventObj, &phtmlElement);
	phtmlElement->get_tagName(&bstrTagName);
	_bstrTagName = bstrTagName;
	phtmlElement->getAttribute(L"type", 0, &varTagType);
	if (VT_BSTR == varTagType.vt)
	{
		_bstrTagType = varTagType.bstrVal;
	}
	else
	{
		_bstrTagType = "";
	}
	phtmlElement->get_id(&bstrID);
	_bstrID = bstrID;
	phtmlElement->get_innerText(&bstrInnerText);
	phtmlElement->get_sourceIndex(&nSourceIndex);
	phtmlElement->get_id(&bstrID);
	_bstrID = bstrID;
	Fire_ElementOnMouseOver(bstrTagName, _bstrTagType.copy(), bstrInnerText, nSourceIndex);
	return;
}


void CIEBrowser::HTMLDocumentEvents_OnSelect(IDispatch *pDisp)
{
	HRESULT hr = 0;
	MSHTML::IHTMLEventObj *piEvent = NULL;
	MSHTML::IHTMLElement *piSrcElement = NULL;
	IDispatch *pdispDoc = NULL;
	MSHTML::IHTMLDocument2 *piDoc2 = NULL;
	MSHTML::IHTMLElement *piSelectionElement = NULL;
	_bstr_t _bstrRangeText;
	VARIANT_BOOL varbAltKey = VARIANT_FALSE;


	GetHTMLInterfacesFromIDispatch(pDisp, &piEvent, &piSrcElement);
	piEvent->get_altKey(&varbAltKey);
	if (VARIANT_TRUE == varbAltKey)
	{
		m_spIEBrowser->get_Document(&pdispDoc);
		pdispDoc->QueryInterface(__uuidof(MSHTML::IHTMLDocument2), (void**) &piDoc2);
		_bstrRangeText = GetActiveSelection(piDoc2, &piSelectionElement);
		
		piDoc2->Release();
		Fire_DocumentSelect(_bstrRangeText.copy(), piSelectionElement);
	}
	piEvent->Release();
	if (NULL != piSrcElement)
	{
		piSrcElement->Release();
	}
	return;
}


_bstr_t CIEBrowser::GetActiveSelection(MSHTML::IHTMLDocument2 *piDoc2,
									   MSHTML::IHTMLElement **piSelectionElement)
{
	HRESULT hr = 0;
	MSHTML::IHTMLDocument2 *piFrameDoc2 = NULL;
	MSHTML::IHTMLFramesCollection2 *piFrameCollection = NULL;
	MSHTML::IHTMLWindow2 *piFrameWindow = NULL;
	MSHTML::IHTMLSelectionObject *piSelection = NULL;
	IDispatch *pdispRange = NULL;
	MSHTML::IHTMLTxtRange *piRange = NULL;
	BSTR bstrRangeText;
	_bstr_t _bstrRangeText = "";
	_bstr_t _bstrRetVal = "";
	long nFrameCollectionLength = 0;
	long i = 0;
	VARIANT varFrameIndex;
	VARIANT varFrameItem;
	
	piDoc2->get_selection(&piSelection);
	piSelection->createRange(&pdispRange);
	pdispRange->QueryInterface(__uuidof(MSHTML::IHTMLTxtRange), (void**)&piRange);
	piRange->get_text(&bstrRangeText);
	piRange->parentElement(piSelectionElement);
	_bstrRangeText = bstrRangeText;

	SysFreeString(bstrRangeText);
	piSelection->Release();
	pdispRange->Release();
	piRange->Release();

	if (_bstr_t((char*)NULL) != _bstrRangeText)
	{
		
		return _bstrRangeText;
	}
	else
	{
		piDoc2->get_frames(&piFrameCollection);
		piFrameCollection->get_length(&nFrameCollectionLength);
		for (i = 0;
			 i < nFrameCollectionLength;
			 ++i)
		{
			VariantInit(&varFrameIndex);
			VariantInit(&varFrameItem);
			varFrameIndex.vt = VT_I4;
			varFrameIndex.lVal = i;			
			piFrameCollection->item(&varFrameIndex,
									&varFrameItem);
			varFrameItem.pdispVal->QueryInterface(MSHTML::IID_IHTMLWindow2,
												  (void**)&piFrameWindow);
			piFrameWindow->get_document(&piFrameDoc2);
			VariantClear(&varFrameItem);
			piFrameWindow->Release();
			_bstrRangeText = GetActiveSelection(piFrameDoc2, piSelectionElement);
			piFrameDoc2->Release();
			if (_bstr_t((char*)NULL) != _bstrRangeText)
			{
				return _bstrRangeText;
			}
		}
		
	}
	return _bstrRangeText;






// 	piFrameDoc2 = piDoc2;
// 	piFrameDoc2->get_frames(&piFrameCollection);
// 	piFrameCollection->get_length(&nFrameCollectionLength);
// 	while (1)
// 	{
// 		piFrameDoc2->get_selection(&piSelection);
// 		piSelection->createRange(&pdispRange);
// 		pdispRange->QueryInterface(__uuidof(MSHTML::IHTMLTxtRange), (void**)&piRange);
// 		piRange->get_text(&bstrRangeText);
// 		_bstrRangeText = bstrRangeText;		

// 		SysFreeString(bstrRangeText);
// 		piSelection->Release();
// 		pdispRange->Release();
// 		piRange->Release();
		
// 		if (_bstr_t((char*)NULL) != _bstrRangeText)
// 		{
// 			_bstrRetVal = _bstrRangeText;
// 			break;
// 		}
// 		if (i == nFrameCollectionLength)
// 		{
// 			break;
// 		}
// 		VariantInit(&varFrameIndex);
// 		VariantInit(&varFrameItem);
// 		varFrameIndex.vt = VT_I4;
// 		varFrameIndex.lVal = i;
// 		piFrameCollection->item(&varFrameIndex,
// 								&varFrameItem);
// 		varFrameItem.pdispVal->QueryInterface(MSHTML::IID_IHTMLWindow2,
// 											  (void**)&piFrameWindow);
// 		piFrameWindow->get_document(&piFrameDoc2);
// 		VariantClear(&varFrameItem);
// 		piFrameWindow->Release();

// 		_bstrRetVal = GetActiveSelection(piFrameDoc2);
// 		if (_bstr_t((char*)NULL) != _bstrRetVal)
// 		{
// 			break;
// 		}
// 		++i;
// 	}
// 	return _bstrRetVal;
}





long CIEBrowser::GetHTMLInterfaces(IDispatch *pdispEvent, MSHTML::IHTMLEventObj **peventEventObj, MSHTML::IHTMLElement **pelemHTML)
{
	HRESULT hr;
	MSHTML::IHTMLElement *pelemChild = NULL;
	MSHTML::IHTMLElement *pelemParent = NULL;
	BSTR bstrTagName;
	_bstr_t _bstrTagName;
	long nReturn = FALSE;

	hr = pdispEvent->QueryInterface(MSHTML::IID_IHTMLEventObj, (void**)peventEventObj);	
	return TRUE;
	
	if (m_pdispMaster != pdispEvent &&
		TRUE == m_nMasterProcess)
	{
		hr = pdispEvent->QueryInterface(MSHTML::IID_IHTMLEventObj, (void**)peventEventObj);
		m_nMasterProcess = FALSE;
		if (SUCCEEDED(hr))
		{
			(*peventEventObj)->get_srcElement(pelemHTML);
			(*pelemHTML)->get_tagName(&bstrTagName);
			_bstrTagName = bstrTagName;
			if (_bstr_t(L"BODY") == _bstrTagName)
			{
				m_nMasterProcess = TRUE;
			}
			nReturn = TRUE;

		}
		nReturn = TRUE;
	}
	return nReturn;
}


HRESULT CIEBrowser::GetHTMLInterfacesFromIDispatch(IDispatch *pDisp, MSHTML::IHTMLEventObj **phtmlEventObj, MSHTML::IHTMLElement **phtmlElement)
{
	HRESULT hr;
	hr = pDisp->QueryInterface(MSHTML::IID_IHTMLEventObj, (void**)phtmlEventObj);
	if (SUCCEEDED(hr))
	{
		hr = (*phtmlEventObj)->get_srcElement(phtmlElement);
	}
	return hr;
}


HRESULT CIEBrowser::GetTagAndType(MSHTML::IHTMLElement *pElem, _bstr_t *_bstrTag, _bstr_t *_bstrType)
{
	BSTR bstrTag;
	VARIANT varType;
	pElem->get_tagName(&bstrTag);
	*_bstrTag = bstrTag;
	pElem->getAttribute(L"type", 0, &varType);
	if (VT_BSTR == varType.vt)
	{
		*_bstrType = varType.bstrVal;
	}
	else
	{
		*_bstrType = L"";
	}
	return S_OK;
}


// emulated WebBrowser methods / properties
STDMETHODIMP CIEBrowser::Navigate2(VARIANT *varURL, VARIANT *varFlags, VARIANT *varTargetFrameName, VARIANT *varPostData, VARIANT *varHeaders)
{
	m_spIEBrowser->Navigate2(varURL, varFlags, varTargetFrameName, varPostData, varHeaders);
	// reset this to 0 - basically resets our event trap (IsolateEvent)
	m_bDispContinue = 0;
	return S_OK;
}

STDMETHODIMP CIEBrowser::get_Document(IDispatch **pVal)
{
	//IDispatch *pdispDocument = NULL;
	m_spIEBrowser->get_Document(pVal);
	return S_OK;
}

STDMETHODIMP CIEBrowser::get_BrowserIDispatch(IDispatch **pVal)
{
	m_spIEBrowser->QueryInterface(IID_IDispatch, (void**)pVal);
	return S_OK;
}

STDMETHODIMP CIEBrowser::put_PopupIDispatch(IDispatch **pVal)
{
	m_pPopupIDispatch = *pVal;
	PostMessage(RIE_POPUPIDISPATCH, 0);
	return S_OK;
}

STDMETHODIMP CIEBrowser::put_MousedownIsMouseclick(BOOL newVal)
{
	m_MousedownIsMouseclick = newVal;
	return S_OK;
}

STDMETHODIMP CIEBrowser::get_MousedownIsMouseclick(BOOL *pVal)
{
	*pVal = m_MousedownIsMouseclick;
	return S_OK;
}

// property: ignoreEvents methods

STDMETHODIMP CIEBrowser::get_ignoreEvents(BOOL *pbVal)
{
	*pbVal = m_bIgnoreEvents;
	return S_OK;
}


STDMETHODIMP CIEBrowser::put_ignoreEvents(BOOL bNewVal)
{
	m_bIgnoreEvents = bNewVal;
	return S_OK;
}


STDMETHODIMP CIEBrowser::TranslateAccelerator(MSG *pMsg)
{
	//int i = 123;
    // m_spWebBrowser is a data member of type IWebBrowser2.
    // Using CComQIPtr in this way queries m_spWebBrowser
    // for the IOleInPlaceActiveObject interface which is
    // then stored in the pIOIPAO variable.
    //
    CComQIPtr<IOleInPlaceActiveObject,
              &IID_IOleInPlaceActiveObject> pIOIPAO(m_spIEBrowser);
 
    HRESULT hr = S_FALSE;
 
    if (pIOIPAO)
    {
		/*n
       MSG msg;
       msg.message = *pMsg;
       msg.wParam = wParam;
       msg.lParam = lParam;
	   */
 
       hr = pIOIPAO->TranslateAccelerator(pMsg);
    }
 
    return hr;	
	//return S_OK;
}






STDMETHODIMP CIEBrowser::get_BrowserID(BSTR *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bstrBrowserID.copy();
	return S_OK;
}

STDMETHODIMP CIEBrowser::put_BrowserID(BSTR newVal)
{
	// TODO: Add your implementation code here
	m_bstrBrowserID = newVal;
	return S_OK;
}


// long CIEBrowser::IsolateEvent(long nDispPtr, MSHTML::IHTMLElement *pelemSrc, MSHTML::IHTMLEventObj *peventEvent)
// {
// 	MSHTML::IHTMLElement *pelemParent = NULL;
// 	VARIANT_BOOL vbCancelBubble;
// 	long bReturn = 1;

// 	pelemSrc->get_parentElement(&pelemParent);
// 	if (NULL == pelemParent)
// 	{
// 		bReturn = 0;
// 	}
// 	else
// 	{
// 		peventEvent->get_cancelBubble(&vbCancelBubble);
// 		if (VARIANT_TRUE == vbCancelBubble)
// 		{
// 			bReturn = 0;
// 		}
// 	}
// 	pelemParent->Release();
// 	return bReturn;
// }



long CIEBrowser::IsolateEvent(long nDispPtr, MSHTML::IHTMLElement *pelemSrc, MSHTML::IHTMLEventObj *peventEvent)
{
	MSHTML::IHTMLElement *pelemParent = NULL;
	VARIANT_BOOL vbCancelBubble;
	long bReturn = 0;
	long nSourceIndex = 0;

	pelemSrc->get_parentElement(&pelemParent);
	if (NULL == pelemParent)
	{
		m_nDispPtr = 0;
		m_bDispContinue = 0;
	}
	else
	{
		peventEvent->get_cancelBubble(&vbCancelBubble);
		if (VARIANT_TRUE == vbCancelBubble)
		{
			m_nDispPtr = nDispPtr;
			m_bDispContinue = 0;
			bReturn = 0;
		}
		else
		{
			BSTR bstrTagName;
			_bstr_t _bstrTagName;
			pelemSrc->get_tagName(&bstrTagName);
			_bstrTagName = bstrTagName;
			ATLTRACE("m_bDispContinue = %d\n", m_bDispContinue);
			ATLTRACE("m_nDispPtr = %d\n", m_nDispPtr);
			ATLTRACE("nDispPtr = %d\n", nDispPtr);
			ATLTRACE("m_nDispID1 = %d\n", m_nDispID1);
			ATLTRACE("m_nDispID2 = %d\n", m_nDispID2);

			pelemSrc->get_sourceIndex(&nSourceIndex);
			switch (m_bDispContinue)
			{
				case 0:
					if (m_nDispPtr == nDispPtr &&
						m_nDispID1 == m_nDispID2)
					{
						bReturn = 1;
					}
					else if (m_nDispPtr != nDispPtr ||
							 0 == m_nDispPtr)
					{
						m_nIsolateSourceIndex = nSourceIndex;
						m_nDispPtr = nDispPtr;
						m_bDispContinue = 1;
					}
					break;
				
				case 1:
					if (nSourceIndex != m_nIsolateSourceIndex)
					{
						m_nIsolateSourceIndex = nSourceIndex;
						bReturn = 0;
					}
					else if (m_nDispPtr != nDispPtr)
					{
						m_nDispPtr = nDispPtr;
						m_bDispContinue = 0;
						bReturn = 1;
					}
					else
					{
						m_bDispContinue = 0;
						bReturn = 1;
					}
					break;
			}
		}
		pelemParent->Release();
	}
	ATLTRACE("bReturn = %d\n\n", bReturn);
	return bReturn;
}


// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/waiting_in_a_message_loop.asp
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/windows_time.asp
STDMETHODIMP CIEBrowser::Sleep(long nMilliseconds)
{
	DWORD nResult = 0;
	DWORD nStart = GetTickCount();
	DWORD nCheck = 0;
	long hBrowser = 0;
	HANDLE phHandles[1];
	MSG msgTemp;
	BOOL bRet = 0;
	IOleWindow *piOleWindow = NULL;
	HWND hwndHandle(NULL);
	DWORD dwReturn = 0;
	HRESULT hr = 0;
	BOOL bBreakSleep = FALSE;
// 	m_spIEBrowser->QueryInterface(__uuidof(IOleWindow), (void**)&piOleWindow);
// 	piOleWindow->GetWindow(&hwndHandle);
// 	piOleWindow->Release();
	phHandles[0] = m_hDocumentComplete;

	while (1)
	{
		if (GetTickCount() - nStart >= nMilliseconds)
		{
			break;
		}
		// read all of the messages, removing each message as we read it
		while (bRet = PeekMessage(&msgTemp, NULL, 0, 0, PM_REMOVE) != 0)
		{
			if (-1 == bRet)
			{
				bBreakSleep = TRUE;
				break;
			}
			if (WM_QUIT == msgTemp.message)
			{
				break;
			}
			TranslateMessage(&msgTemp);
			DispatchMessage(&msgTemp);
		}
		if (TRUE == bBreakSleep)
		{
			break;
		}
		nResult = MsgWaitForMultipleObjectsEx(1, phHandles, nMilliseconds, QS_INPUT, MWMO_INPUTAVAILABLE);
	}
	
	return S_OK;
}





STDMETHODIMP CIEBrowser::GoBack()
{
	m_spIEBrowser->GoBack();
	return S_OK;
	
}


STDMETHODIMP CIEBrowser::GoForward()
{
	m_spIEBrowser->GoForward();
	return S_OK;
}


STDMETHODIMP CIEBrowser::GoHome()
{
	m_spIEBrowser->GoHome();
	return S_OK;
}


STDMETHODIMP CIEBrowser::Stop()
{
	m_spIEBrowser->Stop();
	return S_OK;
}


STDMETHODIMP CIEBrowser::Refresh2(VARIANT *varLevel)
{
	m_spIEBrowser->Refresh2(varLevel);
	// reset this to 0 - basically resets our event trap (IsolateEvent)
	m_bDispContinue = 0;
	return S_OK;
}

// http://msdn2.microsoft.com/en-us/library/ms533843.aspx
int CIEBrowser::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

   Gdiplus::GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }    
   }

   free(pImageCodecInfo);
   return -1;  // Failure
}



// http://www.codeproject.com/internet/htmlimagecapture.asp
// http://www.codeproject.com/bitmap/bitmapdc.asp
STDMETHODIMP CIEBrowser::ScreenCapture(VARIANT_BOOL vbFullScreen,
									   BSTR bstrUploadUrl,
									   BSTR *pbstrEncodedImage)
{
	IDispatch *pdispDoc = NULL;
	MSHTML::IHTMLDocument2 *piDoc = NULL;
	MSHTML::IHTMLDocument3 *piDoc3 = NULL;
	MSHTML::IHTMLElement *piDocElement = NULL;
	MSHTML::IHTMLElement2 *piDocElement2 = NULL;
	MSHTML::IHTMLElement *piBody = NULL;
	MSHTML::IHTMLBodyElement *piBodyElem = NULL;
	MSHTML::IHTMLElement2 *piBody2 = NULL;
	MSHTML::IHTMLElementRender *piRender = NULL;
	MSHTML::IHTMLElement2 *piScrollElement2 = NULL;
	Gdiplus::Bitmap *poDestBitmap = NULL;
	Gdiplus::Bitmap *poTempBitmap = NULL;
	Gdiplus::Graphics *poDestGraphics = NULL;
	Gdiplus::Graphics *poTempGraphics = NULL;
	Gdiplus::EncoderParameters oEncoderParams;
	HDC hDestDC;
	HDC hTempDC;
	CLSID oClsid;
	long nWidth = 0;
	long nHeight = 0;
	long nClientWidth = 0;
	long nClientHeight = 0;
	long nOffsetWidth = 0;
	long nOffsetHeight = 0;
	long nDestX = 0;
	long nDestY = 0;
	long nDestWidth = 0;
	long nDestHeight = 0;	
	long nTempX = 0;
	long nTempY = 0;
	long nRenderWidth = 0;
	long nRenderHeight = 0;
	long nScrollHorizontal = 0;
	long nScrollVertical = 0;
	long nScrollTop = 0;
	long nScrollLeft = 0;
	unsigned long nQuality = 100;
	RECTL oBrowserRect;
	
	m_spIEBrowser->get_Document(&pdispDoc);
	pdispDoc->QueryInterface(__uuidof(MSHTML::IHTMLDocument2), (void**) &piDoc);
	piDoc->get_body(&piBody);
	piBody->QueryInterface(__uuidof(MSHTML::IHTMLBodyElement), (void**) &piBodyElem);
	piBody->QueryInterface(__uuidof(MSHTML::IHTMLElement2), (void**) &piBody2);
	piDoc->QueryInterface(__uuidof(MSHTML::IHTMLDocument3), (void**) &piDoc3);
	piDoc3->get_documentElement(&piDocElement);
	piDocElement->QueryInterface(__uuidof(MSHTML::IHTMLElement2), (void**) &piDocElement2);
	piDocElement2->QueryInterface(__uuidof(MSHTML::IHTMLElementRender), (void**) &piRender);
	piDocElement2->get_scrollWidth(&nWidth);
	piDocElement2->get_scrollHeight(&nHeight);
 	piDocElement2->get_clientWidth(&nClientWidth);
 	piDocElement2->get_clientHeight(&nClientHeight);
	if (0 == nClientWidth &&
		0 == nClientHeight)
	{
		piBody2->get_scrollWidth(&nWidth);
		piBody2->get_scrollHeight(&nHeight);
		piBody2->get_clientWidth(&nClientWidth);
		piBody2->get_clientHeight(&nClientHeight);
		piRender->Release();
		piBody2->QueryInterface(__uuidof(MSHTML::IHTMLElementRender), (void**)&piRender);
		piScrollElement2 = piBody2;
	}
	else
	{
		piScrollElement2 = piDocElement2;
	}
	piScrollElement2->get_scrollTop(&nScrollTop);
	piScrollElement2->get_scrollLeft(&nScrollLeft);

	if (VARIANT_TRUE == vbFullScreen)
	{
		poDestBitmap = new Gdiplus::Bitmap(nWidth, 
										   nHeight, 
										   PixelFormat32bppRGB);
		poDestGraphics = Gdiplus::Graphics::FromImage(poDestBitmap);
		poDestGraphics->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
		poDestGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
		poDestGraphics->SetPixelOffsetMode(Gdiplus::PixelOffsetModeHighQuality);
		hDestDC = poDestGraphics->GetHDC();
	
		poTempBitmap = new Gdiplus::Bitmap(nClientWidth,
										   nClientHeight,
										   PixelFormat32bppRGB);
		poTempGraphics = Gdiplus::Graphics::FromImage(poTempBitmap);
		hTempDC = poTempGraphics->GetHDC();
	
		nScrollHorizontal = (nWidth > nClientWidth) ? nWidth / nClientWidth : 0;
		nScrollVertical = (nHeight > nClientHeight) ? nHeight / nClientHeight : 0;
		nRenderWidth = nClientWidth;
		nRenderHeight = nClientHeight;

		for (long i = 0;
			 i < nScrollVertical + 1; 
			 ++i)
		{
			piScrollElement2->put_scrollTop(i * nClientHeight);

			if (i == nScrollVertical &&
				0 != nScrollVertical)
			{
				nTempY = nDestY - (nHeight - nClientHeight);
				nRenderHeight = nHeight - (nClientHeight * i);
			}

			nDestX = 0;
			nTempX = 0;
			nRenderWidth = nClientWidth;

			for (long j = 0;
				 j < nScrollHorizontal + 1;
				 j++)
			{
				piScrollElement2->put_scrollLeft(j * nClientWidth);
				piRender->DrawToDC((_RemotableHandle*)hTempDC);

				if (j == nScrollHorizontal &&
					0 != nScrollHorizontal)
				{
					nTempX = nDestX - (nWidth - nClientWidth);
					nRenderWidth = nWidth - (nClientWidth * j);
				}
			
				::BitBlt(hDestDC,
						 nDestX,
						 nDestY,
						 nRenderWidth,
						 nRenderHeight,
						 hTempDC,
						 nTempX,
						 nTempY,
						 SRCCOPY);

				nDestX += nClientWidth;
			}

			nDestY += nClientHeight;
		}
		poTempGraphics->ReleaseHDC(hTempDC);
		delete poTempBitmap;
	}
	else
	{
		poDestBitmap = new Gdiplus::Bitmap(nClientWidth,
										   nClientHeight,
										   PixelFormat32bppRGB);
		poDestGraphics = Gdiplus::Graphics::FromImage(poDestBitmap);
		poDestGraphics->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
		poDestGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
		poDestGraphics->SetPixelOffsetMode(Gdiplus::PixelOffsetModeHighQuality);
		hDestDC = poDestGraphics->GetHDC();
		piRender->DrawToDC((_RemotableHandle*)hDestDC);
	}
	poDestGraphics->ReleaseHDC(hDestDC);



	
	UINT nTempWidth = poDestBitmap->GetWidth();
	UINT nTempHeight = poDestBitmap->GetHeight();

	Gdiplus::Bitmap *poThumbnail = new Gdiplus::Bitmap(nTempWidth / 2,
													   nTempHeight / 2,
													   PixelFormat32bppRGB);
	Gdiplus::Graphics *poThumbnailGraphics = Gdiplus::Graphics::FromImage(poThumbnail);
	Gdiplus::Rect oRect = Gdiplus::Rect(0, 0, nTempWidth / 2, nTempHeight / 2);
	poThumbnailGraphics->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
	poThumbnailGraphics->DrawImage(poDestBitmap,
								   oRect,
								   0, 0,
								   nTempWidth,
								   nTempHeight,
								   Gdiplus::UnitPixel,
								   NULL,
								   NULL,
								   NULL);
	nQuality = 100;
	oEncoderParams.Count = 1;
	oEncoderParams.Parameter[0].Guid = Gdiplus::EncoderQuality;
	oEncoderParams.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
	oEncoderParams.Parameter[0].NumberOfValues = 1;	
	oEncoderParams.Parameter[0].Value = &nQuality;

	GetEncoderClsid(L"image/png", &oClsid);
	IStream *piMemStream = NULL;
	HGLOBAL hGlobal;
	hGlobal = ::GlobalAlloc(GHND, 0);
	::CreateStreamOnHGlobal(hGlobal,
							true,
							&piMemStream);
	poThumbnail->Save(piMemStream, &oClsid);
	char *szBase64 = Util::Base64Encode((unsigned char*)::GlobalLock(hGlobal),
										::GlobalSize(hGlobal));
	CComBSTR oBase64Bstr = szBase64;
	*pbstrEncodedImage = oBase64Bstr.Detach();
	delete[] szBase64;
	int nBase64Length = strlen(szBase64);
	::GlobalUnlock(hGlobal);
	piMemStream->Release();
	::GlobalFree(hGlobal);
	delete poDestBitmap;
	delete poDestGraphics;
	delete poThumbnail;
	delete poThumbnailGraphics;



// // start here
// 	char *szEncodedBase64 = Util::UriEncode(szBase64, nBase64Length);
// 	char szPostPre[43] = "action=add&imagename=foo.png&encodedimage=";
// 	char *szPostBody = new char[strlen(szEncodedBase64) + strlen(szPostPre) + 1];
// 	memset((void*)szPostBody, 0, sizeof(char) * (strlen(szEncodedBase64) + strlen(szPostPre) + 1));
// 	strcpy(szPostBody, szPostPre);
// 	strcpy(szPostBody + strlen(szPostPre), szEncodedBase64);
//  	delete []szBase64;
// 	delete []szEncodedBase64;
// 	WCHAR *pwszPostBody = NULL;
// 	int nPostBodyLength = strlen(szPostBody);
// 	int nCount = MultiByteToWideChar(CP_ACP,
// 									 0,
// 									 szPostBody,
// 									 nPostBodyLength,
// 									 pwszPostBody,
// 									 0);
// 	pwszPostBody = new WCHAR[nCount + 1];
// 	memset((void*)pwszPostBody, 0, sizeof(WCHAR) * (nCount + 1));
// 	nCount = MultiByteToWideChar(CP_ACP,
// 								 0,
// 								 szPostBody,
// 								 nPostBodyLength,
// 								 pwszPostBody,
// 								 nCount);
// 	delete []szPostBody;
// 	HRESULT hr = 0;
// 	MSXML2::IXMLHTTPRequest *piXHR = NULL;
// 	hr = CoCreateInstance(CLSID_XMLHTTPRequest,
// 						  NULL,
// 						  CLSCTX_INPROC,
// 						  IID_IXMLHttpRequest,
// 						  reinterpret_cast<void**>(&piXHR));
// 	SUCCEEDED(hr) ? 0 : throw hr;
// 	hr = piXHR->open(L"POST",
// 					 bstrUploadUrl,
// 					 _variant_t(VARIANT_FALSE),
// 					 _variant_t(L""),
// 					 _variant_t(L""));
// 	SUCCEEDED(hr) ? 0 : throw hr;
// 	hr = piXHR->setRequestHeader(L"Content-Type",
// 								 L"application/x-www-form-urlencoded");
// 	SUCCEEDED(hr) ? 0 : throw hr;
// 	hr = piXHR->send(_variant_t(pwszPostBody));
// 	delete []pwszPostBody;
// 	SUCCEEDED(hr) ? 0 : throw hr;
// 	hr = piXHR->get_responseText(pbstrEncodedImage);
// 	SUCCEEDED(hr) ? 0 : throw hr;	
// 	piXHR->Release();
	
	piScrollElement2->put_scrollTop(nScrollTop);
	piScrollElement2->put_scrollLeft(nScrollLeft);

	pdispDoc->Release();
	piDoc->Release();
	piBody->Release();
	piBodyElem->Release();
	piBody2->Release();
	piDoc3->Release();
	piDocElement->Release();
	piDocElement2->Release();
	piRender->Release();
	
	return S_OK; 
}


STDMETHODIMP CIEBrowser::SendKeys(BSTR bstrValue)
{

	INPUT inpKeys[2];
	unsigned long nValueLength = 0;
	wchar_t *wcBuffer = NULL;
	unsigned long nCount = 0;
	SHORT nScanCode = 0;
	_bstr_t _bstrTemp = bstrValue;
	
	wcBuffer = (wchar_t*)_bstr_t(bstrValue);
	nValueLength = SysStringLen(bstrValue);
	for (unsigned long i = 0;
		 i < nValueLength;
		 ++i)
	{
		memset(inpKeys, 0, sizeof(INPUT) * 2);		
		inpKeys[0].type = INPUT_KEYBOARD;
		if (' ' <= wcBuffer[i] &&
			'~' >= wcBuffer[i])
		{
			inpKeys[0].ki.dwFlags = KEYEVENTF_UNICODE;
			inpKeys[1] = inpKeys[0];
			inpKeys[1].ki.dwFlags |= KEYEVENTF_KEYUP;
			inpKeys[0].ki.wScan = inpKeys[1].ki.wScan = wcBuffer[i];
		}
		else
		{
			inpKeys[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
			inpKeys[1] = inpKeys[0];
			inpKeys[1].ki.dwFlags |= KEYEVENTF_KEYUP;
			inpKeys[0].ki.wVk = inpKeys[1].ki.wVk = VkKeyScan(wcBuffer[i]);
		}
		inpKeys[0].ki.time = inpKeys[1].ki.time = GetTickCount();
 		nCount = SendInput(2, inpKeys, sizeof(INPUT));
		//Sleep(500);
	}
	return S_OK;
}


// IPersistPropertyBag methods

STDMETHODIMP CIEBrowser::InitNew()
{
	return S_OK;
}


STDMETHODIMP CIEBrowser::Load(IPropertyBag *ppropBag, IErrorLog *perrorLog)
{
	if (NULL == ppropBag)
	{
		return E_POINTER;
	}
	VARIANT varExpire;
	HRESULT hr;
	
	VariantInit(&varExpire);
	hr = ppropBag->Read(L"Expire", &varExpire, 0);
	
	VariantClear(&varExpire);

	return S_OK;

}


STDMETHODIMP CIEBrowser::Save(IPropertyBag *ppropBag, BOOL bClearDirty, BOOL bSaveAll)
{
	return S_OK;
}

STDMETHODIMP CIEBrowser::GetClassID(CLSID *pClassID)
{
	if (0 == pClassID)
	{
		return E_FAIL;
	}
	*pClassID = GetObjectCLSID();
	return S_OK;
}

